<?php
require_once(APPPATH . '/libraries/Zend/Acl.php');
require_once(APPPATH . '/libraries/Zend/Acl/Role.php');
require_once(APPPATH . '/libraries/Zend/Acl/Resource.php');

class Security_Manager {
	
	const IS_LOGGED_KEY = 'is_logged_key__';
	const VISITOR_KEY = 'visitor_key__';
	
	const DEFAULT_ANONYMOUS_ROLE = 'guest';
	const DEFAULT_VISITOR_ROLE = 'user';
	
	/**
	 * @var Security_Manager
	 */
	private static $instance = null;
	
	private $session;
	private $zend_acl;
	private $resources;
	private $mvc_resources;
	private $default;
	
	private $visitor;
	
	public function __construct() {
		if (self::$instance != null) return;
		self::$instance = $this;
		
		$CI = &get_instance();
		$this->session = &$CI->session;
		$CI->load->library('security/security_visitor');
		
		$this->setup_ACL($CI->config->item('acl'));
		$this->setup_visitor();
	}
	
	public static function save($user_data) {
		if (Console::isTraceEnabled()) {
			Console::trace('Saving visitor '. $user_data['email']);
		}
		
		$roles = array();
		
		foreach($user_data['roles'] as $role) {
			$roles[] = $role['role'];
		}
		
		self::$instance->visitor = new Security_Visitor(array(
					'id' => $user_data['id'],
					'email' => $user_data['email'],
					'roles' => $roles));
		
		self::$instance->session
			->set_userdata(self::VISITOR_KEY, self::$instance->visitor->to_array());
	}
	
	public static function remove() {
		if (Console::isTraceEnabled()) {
			Console::trace('Removing visitor '
				. self::$instance->visitor->email);
		}
		
		self::$instance->session->unset_userdata(self::VISITOR_KEY);
		self::$instance->setup_visitor();
	}
	
	public static function mvc_authorization() {
		$resource_name = '__MVC_'. Application::get_called_controller()
			.'_'. Application::get_called_method() .'__';
		
		if (Console::isTraceEnabled()) {
			Console::trace('MVC Authorization '. $resource_name);
		}
		
		if (!array_key_exists($resource_name, self::$instance->mvc_resources)) {
			return (boolean) self::$instance->default['allow'];
		}
		
		$resource = self::$instance->mvc_resources[$resource_name];
		if (isset($resource['methods']) && !empty($resource['methods'])) {
			$allowed_method = false;
			
			foreach ($resource['methods'] as $method) {
				switch ($method) {
					case 'post':
						if (Application::is_request_post()) {
							$allowed_method = true;
						}
					break;
					
					case 'get':
						if (Application::is_request_get()) {
							$allowed_method = true;
						}
						break;
				}
				
				if ($allowed_method) break;
			}
			
			if (!$allowed_method) return false;
		}
		
		return self::$instance->authorize($resource_name);
	}
	
	public static function authorize_resource($resource_name) {
		if (Console::isTraceEnabled()) {
			Console::trace('Resource Authorization '. $resource_name);
		}
		
		if (!in_array($resource_name, self::$instance->resources)) {
			return (boolean) self::$instance->default['allow'];
		}
		
		return self::$instance->authorize($resource_name);
	}
	
	public static function get_redirect_url() {
		return self::$instance->default['redirect_url'];
	}
	
	public static function has_role($role) {
		return self::$instance->visitor->has_role($role);
	}
	
	public static function get_visitor() {
		return self::$instance->visitor;
	}
	
	public static function get_password_hash($hash) {
		return md5($hash);
	}
	
	private function authorize($resource_name) {
		foreach ($this->visitor->roles as $role) {
			if ($this->zend_acl->isAllowed($role, $resource_name)) {
				return true;
			}
		}
	
		return false;
	}
	
	private function setup_visitor() {
		$visitor_data = $this->session->userdata(self::VISITOR_KEY);
		
		if (empty($visitor_data)) {
			$this->visitor = new Security_Visitor(array(
					'roles' => array(self::DEFAULT_ANONYMOUS_ROLE)));
		} else {
			$this->visitor = new Security_Visitor($visitor_data);
		}
		
		Console::var_dump($this->visitor);
	}
	
	private function setup_ACL($config) {
		$this->zend_acl = new Zend_Acl();
		
		foreach ($config['roles'] as $role => $cfg) {
			if (isset($cfg['parents']) && !is_array($cfg['parents'])) {
				$this->zend_acl->addRole(
						new Zend_Acl_Role($role), $cfg['parents']);
			} else {
				$this->zend_acl->addRole(new Zend_Acl_Role($role));
			}
		}
		
		$this->resources = array();
		
		foreach ($config['resources'] as $resource => $cfg) {
			$this->resources[] = $resource;
			$this->zend_acl->addResource($resource);
			
			if (isset($cfg['deny']) && is_array($cfg['deny'])) {
				foreach ($cfg['deny'] as $role) {
					$this->zend_acl->deny($role, $resource);
				}
			}
			
			if (isset($cfg['allow']) && is_array($cfg['allow'])) {
				foreach ($cfg['allow'] as $role) {
					$this->zend_acl->allow($role, $resource);
				}
			}
		}
		
		$this->mvc_resources = array();
		
		foreach ($config['mvc'] as $controller => $methods) {
			foreach ($methods as $method => $cfg) {
				$resource = '__MVC_'. $controller .'_'. $method .'__';
				$this->mvc_resources[$resource] = $cfg;
				$this->zend_acl->addResource($resource);
				
				if (isset($cfg['deny']) && is_array($cfg['deny'])) {
					foreach ($cfg['deny'] as $role) {
						$this->zend_acl->deny($role, $resource);
					}
				}
				
				if (isset($cfg['allow']) && is_array($cfg['allow'])) {
					foreach ($cfg['allow'] as $role) {
						$this->zend_acl->allow($role, $resource);
					}
				}
			}
		}
		
		
		
		$this->default = $config['default'];
	}
}